package cn.com.greatwall.flowable.rest;

import java.io.InputStream;
import java.util.Base64;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.flowable.bpmn.model.ValuedDataObject;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.FormService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.collect.ImmutableMap;

import cn.com.greatwall.common.utils.SecurityUtils;
import cn.com.greatwall.flowable.service.PermissionService;
import cn.com.greatwall.flowable.service.ProcessDefinitionService;
import cn.com.greatwall.flowable.service.dto.ProcessDefinitionDto;
import cn.com.greatwall.flowable.service.dto.criteria.ProcessDefinitionQueryCriteria;
import cn.com.greatwall.logging.annotation.Log;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author herw
 * @Time 2020-11-17 14:42:04
 * @Version 1.0
 * @Description: TODO(用一句话描述该文件做什么)
 */
@Slf4j
@RestController
@RequiredArgsConstructor
@Api(tags = "Flowable：流程定义")
@RequestMapping("/api/flowable/definition")
public class ProcessDefinitionController {
    private final ProcessDefinitionService processDefinitionService;
    private final FormService formService;
    private final RepositoryService repositoryService;
    private final PermissionService permissionService;

    @Log("查询模型")
    @ApiOperation("查询模型")
    @GetMapping
    @PreAuthorize("@el.check('definition:list')")
    public ResponseEntity<Object> query(ProcessDefinitionQueryCriteria criteria, Pageable pageable) {
        return new ResponseEntity<>(processDefinitionService.queryAll(criteria, pageable), HttpStatus.OK);
    }

    @Log("查询授权流程")
    @ApiOperation("查询授权流程")
    @GetMapping(value = "/startableUser")
    @PreAuthorize("@el.check('definition:list')")
    public ResponseEntity<Object> queryStartableUser(ProcessDefinitionQueryCriteria criteria, Pageable pageable) {
        return new ResponseEntity<>(processDefinitionService.queryStartableUser(criteria, pageable), HttpStatus.OK);
    }

    @Log("删除流程定义")
    @ApiOperation("删除流程定义")
    @DeleteMapping
    @PreAuthorize("@el.check('definition:del')")
    public ResponseEntity<Object> delete(@RequestParam String processDefinitionId,
            @RequestParam(required = false, defaultValue = "false") Boolean cascade) {
        processDefinitionService.delete(processDefinitionId, cascade);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("查询流程表单")
    @ApiOperation("查询流程表单")
    @GetMapping(value = "/renderedStartForm")
    @PreAuthorize("@el.check('definition:list')")
    public ResponseEntity<Object> renderedStartForm(@RequestParam String processDefinitionId) {
        permissionService.validateReadPermissionOnProcessDefinition(SecurityUtils.getCurrentUserId().toString(),
                processDefinitionId);
        Object renderedStartForm = formService.getRenderedStartForm(processDefinitionId);
        boolean showBusinessKey = this.isShowBusinessKey(processDefinitionId);
        return new ResponseEntity<>(
                ImmutableMap.of("renderedStartForm", renderedStartForm, "showBusinessKey", showBusinessKey),
                HttpStatus.OK);
    }

    @Log("激活流程定义")
    @ApiOperation("激活流程定义")
    @PutMapping(value = "/activate")
    @PreAuthorize("@el.check('definition:edit')")
    public ResponseEntity<Object> activate(@Validated @RequestBody ProcessDefinitionDto resources) {
        processDefinitionService.activate(resources);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("挂起流程定义")
    @ApiOperation("挂起流程定义")
    @PutMapping(value = "/suspend")
    @PreAuthorize("@el.check('definition:edit')")
    public ResponseEntity<Object> suspend(@Validated @RequestBody ProcessDefinitionDto resources) {
        processDefinitionService.suspend(resources);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("查询流程图")
    @ApiOperation("查询流程图")
    @GetMapping(value = "/image")
    @PreAuthorize("@el.check('definition:edit')")
    public ResponseEntity<Object> image(@RequestParam String processDefinitionId) {
        permissionService.validateReadPermissionOnProcessDefinition(SecurityUtils.getCurrentUsername(),
                processDefinitionId);
        ProcessDefinition processDefinition = processDefinitionService.findById(processDefinitionId);

        InputStream is = repositoryService.getProcessDiagram(processDefinition.getId());
        if (is == null) {
            throw new FlowableException("Process definition image is not found with id " + processDefinitionId);
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);

        try {
            return new ResponseEntity<>(Base64.getEncoder().encodeToString(IOUtils.toByteArray(is)), headers,
                    HttpStatus.OK);
        } catch (Exception e) {
            throw new FlowableException("Process definition image read error with id " + processDefinitionId + e);
        }
    }

    @Log("导出流程XML")
    @ApiOperation("导出流程XML")
    @GetMapping(value = "/exportXML")
    @PreAuthorize("@el.check('definition:edit')")
    public ResponseEntity<byte[]> exportXML(@RequestParam String processDefinitionId) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.TEXT_XML);
        try {
            return new ResponseEntity<>(IOUtils.toByteArray(processDefinitionService.exportXML(processDefinitionId)),
                    headers, HttpStatus.OK);
        } catch (Exception e) {
            log.error("获取流程定义XML信息异常", e);
            throw new FlowableException("ProcessDefinition xml read error with id " + processDefinitionId + e);
        }
    }

    /**
     * 判断流程是否有 业务 key
     * 
     * @param processDefinitionId
     * @return
     */
    private Boolean isShowBusinessKey(String processDefinitionId) {
        List<ValuedDataObject> objs = repositoryService.getBpmnModel(processDefinitionId).getMainProcess()
                .getDataObjects();
        if (objs != null && objs.size() > 0) {
            for (ValuedDataObject valuedDataObject : objs) {
                if ("showBusinessKey".equals(valuedDataObject.getId())) {
                    if (valuedDataObject.getValue() instanceof String) {
                        return Boolean.valueOf((String) valuedDataObject.getValue());
                    } else if (valuedDataObject.getValue() instanceof Boolean) {
                        return (Boolean) valuedDataObject.getValue();
                    } else {
                        return false;
                    }
                }
            }
        }
        return false;
    }
}
